using System;
using System.ComponentModel;
using System.Reflection;
using MSOutlook = Microsoft.Office.Interop.Outlook;

namespace Microsoft.Sample.Outlook.Utility
{
	/// <summary>
	/// Generic wrapper for outlook items (mail, task, contact, etc).
	/// Adapts the item events to include sender object in delegate definition as per .NET event standards.
	/// Implements methods a small subset of methods shared by all outlook items, such as Save, Class and EntryID
	/// </summary>
    public sealed class ItemAdapter
	{
		MSOutlook.ItemEvents_10_Event _outlookItem;
		Type _outlookItemType;

		public event CancelEventHandler Opening;
		public event CancelEventHandler Writing;
		public event CancelEventHandler Closing;
		public event CancelEventHandler Deleting;

		public static ItemAdapter FromObject(object obj)
		{
			if (obj is MSOutlook.ItemEvents_10_Event)
				return new ItemAdapter((MSOutlook.ItemEvents_10_Event)obj);

			return null;

		}

		public ItemAdapter(MSOutlook.ItemEvents_10_Event item)
		{
			_outlookItem = item;
			_outlookItemType = _outlookItem.GetType();

			_outlookItem.Open += new MSOutlook.ItemEvents_10_OpenEventHandler(OnOpen);
            _outlookItem.Close += new MSOutlook.ItemEvents_10_CloseEventHandler(OnClose);
			_outlookItem.Write += new MSOutlook.ItemEvents_10_WriteEventHandler(OnWrite);
            _outlookItem.BeforeDelete += new MSOutlook.ItemEvents_10_BeforeDeleteEventHandler(OnDelete);
		}

		void OnClose(ref bool cancel)
		{
			CancelEventArgs args = new CancelEventArgs(cancel);
			Closing(this, args);
			cancel = args.Cancel;
		}

		void OnDelete(object Item, ref bool cancel)
		{
			CancelEventArgs args = new CancelEventArgs(cancel);
			Deleting(this, args);
			cancel = args.Cancel;
		}

		void OnWrite(ref bool cancel)
		{
			CancelEventArgs args = new CancelEventArgs(cancel);
			Writing(this, args);
			cancel = args.Cancel;
		}

		void OnOpen(ref bool cancel)
		{
			CancelEventArgs args = new CancelEventArgs(cancel);
			Opening(this, args);
			cancel = args.Cancel;
		}

		public MSOutlook.UserProperties UserProperties
		{
			get { return (MSOutlook.UserProperties)GetProperty("UserProperties"); }
		}

		public MSOutlook.ItemProperties ItemProperties
		{
			get { return (MSOutlook.ItemProperties)GetProperty("ItemProperties"); }
		}

		public string EntryID
		{
			get { return (string)GetProperty("EntryID"); }
		}

		public object Parent
		{
			get { return GetProperty("Parent"); }
		}

		public MSOutlook.OlObjectClass Class
		{
			get { return (MSOutlook.OlObjectClass)GetProperty("Class"); }
		}

		public void Save()
		{
			InvokeMethod("Save");
		}

		public void Delete()
		{
			InvokeMethod("Delete");
		}

		public void Display()
		{
			InvokeMethod("Display");
		}



		internal object GetProperty(string name)
		{
			return _outlookItemType.InvokeMember(
				name,
				BindingFlags.GetProperty,
				null, _outlookItem, null);
		}

		internal void SetProperty(string name, object value)
		{
			_outlookItemType.InvokeMember(
				name,
				BindingFlags.SetProperty,
				null, _outlookItem, new object[] { value });
		}

		internal object InvokeMethod(string name, params object[] parameters)
		{
			return _outlookItemType.InvokeMember(
				name,
				BindingFlags.InvokeMethod,
				null, _outlookItem, parameters);
		}

		public MSOutlook.AppointmentItem GetAsAppointment()
		{
			if (this.Class != MSOutlook.OlObjectClass.olAppointment)
				throw new ApplicationException(string.Format("Can't GetAsAppointment for {0}", this.Class));

			return (MSOutlook.AppointmentItem)_outlookItem;
		}

		public MSOutlook.TaskItem GetAsTask()
		{
			if (this.Class != MSOutlook.OlObjectClass.olTask)
				throw new ApplicationException(string.Format("Can't GetAsTask for {0}", this.Class));

			return (MSOutlook.TaskItem)_outlookItem;
		}

		public MSOutlook.PostItem GetAsPost()
		{
			if (this.Class != MSOutlook.OlObjectClass.olPost)
				throw new ApplicationException(string.Format("Can't GetAsPost for {0}", this.Class));

			return (MSOutlook.PostItem)_outlookItem;
		}

		public MSOutlook.MailItem GetAsMail()
		{
			if (this.Class != MSOutlook.OlObjectClass.olMail)
				throw new ApplicationException(string.Format("Can't GetAsMail for {0}", this.Class));

			return (MSOutlook.MailItem)_outlookItem;
		}

		//Office Object
		//Parent

		//Outlook Object
		//Application
		//Class
		//Session

		//Base Item 
		//Close
		//Copy
		//Delete
		//Display
		//GetInspector
	}
}
